package br.org.bertol.mestrado.engine.optimisation.sls;

import java.util.Collections;

import org.apache.log4j.Logger;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.exception.HeuristicsException;
import br.org.bertol.mestrado.engine.optimisation.entity.AbstractUnit;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;

/**
 * @author contaqualquer
 * @param <U>
 */
public class IteratedLocalSearch<U extends AbstractUnit> extends
        AbstractStochasticLocalSearch<U> {

    /**
     * @param verify
     *            Objeto verify
     * @param unit
     *            unidade a ser melhorada
     * @param breakCondition
     *            Número máximo de iterações
     */
    public IteratedLocalSearch(final Verify verify, final U unit,
        final int breakCondition) {

        super(verify, breakCondition, unit);

    }

    /**
     * {@inheritDoc}
     * @see br.org.bertol.mestrado.engine.optimisation.Tecninca#evaluate()
     */
    @SuppressWarnings("unchecked")
    @Override
    public final void evaluate() throws HeuristicsException {

        try {
            betterUnit = (U) startUnit.clone();

            Logger
                    .getLogger(getClass())
                    .info(
                          "ILS START ----------------------------------------- : "
                                  + startUnit);

            int rangeSwap = 1;

            int iter = 0;

            int melhorIter = 0;

            // loop principal
            while (iter - melhorIter < numIteracoes) {
                iter++;

                final int numTrocasMax = rangeSwap + 1;

                int numTrocas = 0;

                U candidatoS = (U) betterUnit.clone();

                // quantidade de perturbar��es que ser�o realizadas
                while (numTrocas < numTrocasMax) {
                    numTrocas++;

                    final int pointer1 = (int) (Math.random() * verify
                            .getClasses().size());

                    int pointer2 = pointer1;

                    while (pointer1 == pointer2) {
                        pointer2 = (int) (Math.random() * verify.getClasses()
                                .size());
                    }

                    Collections.swap(candidatoS.getRoute(), pointer1, pointer2);

                    hillClimbing.setPath(candidatoS);

                    hillClimbing.runHillClimbing();

                    final U candidatoSs = (U) hillClimbing.getBestPath()
                            .clone();

                    if (paretoDominace.compare(candidatoSs.getHeight(),
                                               candidatoS.getHeight()) == ParetoDominace.LEFT_DOMINE) {

                        candidatoSs.update();

                        numTrocas = 0;

                        rangeSwap = 1;

                        betterUnit = (U) candidatoSs.clone();

                        candidatoS = (U) candidatoSs.clone();

                        melhorIter = iter;

                        Logger
                                .getLogger(getClass())
                                .debug(
                                       "Caminho melhor encontrado ------------------------ : "
                                               + candidatoSs);
                    }

                    hillClimbing.reset();
                    numTrocas++;

                }

                rangeSwap++;
            }

            Logger
                    .getLogger(getClass())
                    .info(
                          "ILS STOP ------------------------------------------ : "
                                  + betterUnit);
        } catch (Exception e) {
            throw new HeuristicsException(e);
        }
    }
}
